package com.yoooya.base.third.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.yoooya.base.dao.third.AccessTokenMapper;
import com.yoooya.base.model.po.third.AccessTokenPo;
import com.yoooya.base.third.service.CommonService;
import com.yoooya.base.utils.DateUtils;
import com.yoooya.base.utils.HttpClientUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 微信帮助类
 * 
 * @创建人 Sun Yijing
 * @创建时间 2018-07-08
 */
@Component
public class WxHelper {

	@Value("${wx.mp.id}")
	private String mpId;
	@Value("${wx.mp.secret}")
	private String mpSecret;
	@Value("${wx.token}")
	private String mpToken;
	@Value("${wx.mch.id}")
	private String mchId;
	@Value("${wx.pay.api.key}")
	private String payApiKey;
	@Value("${wx.pay.cert.key}")
	private String certKey;

	@Value("${wx.cp.corpId}")
	private String corpId;
	@Value("${wx.cp.token}")
	private String token;
	@Value("${wx.cp.aesKey}")
	private String aesKey;
	private XStream xStream;
	@Value("${server.domain}")
	private String serverDomain;
	private AccessToken accessToken;
	private JsApiTicket jsApiTicket;
	@Autowired
	private AccessTokenMapper tokenMapper;
	@Autowired
	private CommonService commonService;
	private final Logger logger = LoggerFactory.getLogger(getClass());

	@PostConstruct
	private void init() {
		xStream = new XStream(new XppDriver() {
			public HierarchicalStreamWriter createWriter(Writer out) {
				return new PrettyPrintWriter(out) {

					boolean cdata;

					@Override
					public void setValue(String text) {
						if (StringUtils.isNotEmpty(text)) {
							// 浮点型判断
							Pattern patternFloat = Pattern.compile("[0-9]*(\\.?)[0-9]*");
							// 整型判断
							Pattern patternInt = Pattern.compile("[0-9]+");
							// 如果是整数或浮点数 就不要加[CDATA[]了
							if (patternInt.matcher(text).matches() || patternFloat.matcher(text).matches()) {
								cdata = false;
							} else {
								cdata = true;
							}
						}
						super.setValue(text);
					}

					protected void writeText(QuickWriter writer, String text) {
						if (cdata) {
							writer.write("<![CDATA[");
							writer.write(text);
							writer.write("]]>");
						} else {
							writer.write(text);
						}
					}
				};

			}
		});
		xStream.autodetectAnnotations(true);
	}

	public String toXml(Object msg) {
		return xStream.toXML(msg);
	}

	public Map<String, String> parseXml(String xml) throws DocumentException {
		Map<String, String> map = new HashMap<>();
		Document document = DocumentHelper.parseText(xml);
		Element root = document.getRootElement();
		List<Element> elements = root.elements();
		for (Element e : elements) {
			map.put(e.getName(), e.getText());
		}
		return map;
	}

	public String requestGet(String url) throws IOException {
		CloseableHttpClient client = HttpClientUtils.createSSLClientDefault();
		HttpGet get = new HttpGet(url);
		CloseableHttpResponse response = client.execute(get);
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			return EntityUtils.toString(entity, StandardCharsets.UTF_8);
		}
		return null;
	}

	public String requestPost(String url, String outputStr) throws IOException {
		CloseableHttpClient client = HttpClientUtils.createSSLClientDefault();
		HttpPost post = new HttpPost(url);
		StringEntity stringEntity = new StringEntity(outputStr, StandardCharsets.UTF_8);
		post.setEntity(stringEntity);
		CloseableHttpResponse response = client.execute(post);
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			return EntityUtils.toString(entity, StandardCharsets.UTF_8);
		}
		return null;
	}

	public String getOpenId(String code) {
		String openId = null;
		try {
			String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + getMpId() + "&secret="
					+ getMpSecret() + "&code=" + code + "&grant_type=authorization_code";
			String access_token = requestGet(url);
			JSONObject json = JSON.parseObject(access_token);
			openId = json.getString("openid");
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
		return openId;
	}

	public String getAccessToken() throws Exception {
		Long tokenId = 100l;
		AccessTokenPo token = this.commonService.getAccessToken(tokenId);
		if (null == token || token.isExpires() || StringUtils.isBlank(token.getToken())) {
			String url = "https://api.weixin.qq.com/cgi-bin/token" + "?grant_type=client_credential&appid=" + getMpId()
					+ "&secret=" + getMpSecret();
			try {
				String access_token = requestGet(url);
				System.out.println("access_token:" + access_token);
				if (StringUtils.contains(access_token, "access_token")) {
					accessToken = JSON.parseObject(access_token, AccessToken.class);
					this.commonService.updateToken(tokenId, accessToken.getToken(), accessToken.getExpiresIn());
					token.setToken(accessToken.getToken());
				} else {
					logger.error("获取access_token失败.[{}]", access_token);
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}
		return null == token ? null : token.getToken();
	}

	public void resetAccessToken() {
		Long tokenId = 1l;
		String url = "https://api.weixin.qq.com/cgi-bin/token" + "?grant_type=client_credential&appid=" + getMpId()
				+ "&secret=" + getMpSecret();
		try {
			String access_token = requestGet(url);
			if (StringUtils.contains(access_token, "access_token")) {
				accessToken = JSON.parseObject(access_token, AccessToken.class);
				this.commonService.updateToken(tokenId, accessToken.getToken(), accessToken.getExpiresIn());
				/**
				 * AccessTokenLogPo log = new AccessTokenLogPo(); log.setId(IdUtils.id());
				 * log.setToken(accessToken.getToken());
				 * log.setExpiresIn(accessToken.getExpiresIn());
				 * log.setTime(DateUtils.getSecondTime()); log.setPlatform(platform);
				 * log.setCreateTime(new Date()); tokenMapper.addTokenLog(log);
				 **/
			} else {
				logger.error("获取access_token失败.[{}]", access_token);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	public String getJsApiTicket() throws Exception {
		if (null == jsApiTicket || jsApiTicket.isExpires()) {
			String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + getAccessToken()
					+ "&type=jsapi";
			try {
				String jsapi_ticket = requestGet(url);
				logger.trace("jsapi_ticket={}", jsapi_ticket);
				jsApiTicket = JSON.parseObject(jsapi_ticket, JsApiTicket.class);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}
		return null == jsApiTicket ? null : jsApiTicket.getTicket();
	}

	public String getServerDomain() {
		return serverDomain;
	}

	public String getToken() {
		return token;
	}

	public String getAesKey() {
		return aesKey;
	}

	public String getCorpId() {
		return corpId;
	}

	public String getMpId() {
		return mpId;
	}

	public String getMpSecret() {
		return mpSecret;
	}

	public String getMpToken() {
		return mpToken;
	}

	public String getMchId() {
		return mchId;
	}

	public String getPayApiKey() {
		return payApiKey;
	}

	public String getCertKey() {
		return certKey;
	}
	

	public static class AccessToken {

		@JSONField(name = "access_token")
		private String token;
		@JSONField(name = "expires_in")
		private int expiresIn;
		private long time = DateUtils.getSecondTime();

		public boolean isExpires() {
			return DateUtils.getSecondTime() - time > expiresIn - 120;
		}

		public String getToken() {
			return token;
		}

		public void setToken(String token) {
			this.token = token;
		}

		public int getExpiresIn() {
			return expiresIn;
		}

		public void setExpiresIn(int expiresIn) {
			this.expiresIn = expiresIn;
		}
	}

	public static class JsApiTicket {
		private String ticket;
		@JSONField(name = "expires_in")
		private int expiresIn;
		private long time = DateUtils.getSecondTime();

		public boolean isExpires() {
			return DateUtils.getSecondTime() - time > expiresIn - 120;
		}

		public long getExpiresIn() {
			return expiresIn;
		}

		public void setExpiresIn(int expiresIn) {
			this.expiresIn = expiresIn;
		}

		public String getTicket() {
			return ticket;
		}

		public void setTicket(String ticket) {
			this.ticket = ticket;
		}
	}
}
